package cn.bonoon.kernel.support.services.handler;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import cn.bonoon.kernel.events.OperateEvent;
import cn.bonoon.kernel.support.entities.EntityTree;
import cn.bonoon.kernel.support.entities.Persistable;

/**
 * <pre>
 * 对树形结构的移动都应该区分是在根节点上进行的还是子节点上进行的
 * </pre>
 * @author jackson
 *
 */
public abstract class AbstractTreeMovableHandler<E extends EntityTree<E>> implements OperateHandler{
	protected final Class<E> entityClass;
	private final String selectChildren;
	protected AbstractTreeMovableHandler(Class<E> entityClass){
		this.entityClass = entityClass;
		selectChildren = "select x from " + entityClass.getName() + " x where x.parent.id=?";
	}
	
	@Override
	public Object handler(EntityManager entityManager, OperateEvent event, Persistable entity) {
		E source = entityClass.cast(entity);
		E oldParent = source.getParent();
		if(null == oldParent){
			return move(source, entityManager);
		}else{
			return move(source, entityManager, oldParent);
		}
	}
	
	protected Object move(E source, EntityManager entityManager){
		return null;
	}
	
	protected Object move(E source, EntityManager entityManager, E oldParent){
		return null;
	}
	
	@Override
	public MultiOperateHandlerExecuter handler(final EntityManager entityManager, final OperateEvent event) {
		return new MultiOperateHandlerExecuter(){
			E source;
			E target;
			
			@Override
			public MultiOperateHandlerExecuter addParameter(Persistable entity) {
				if(null == source){
					source = entityClass.cast(entity);
				}else if(null == target){
					target = entityClass.cast(entity);
				}else{
					throw new RuntimeException("参数不正确！");
				}
				return this;
			}

			@Override
			public void execute() {
				if(target.getXpath().startsWith(source.getXpath())){
					throw new RuntimeException("不允许把父节点移动到其子节点！");
				}
				E oldParent = source.getParent();
				if(null == oldParent){
					move(source, target, entityManager);
				}else{
					move(source, target, entityManager, oldParent);
				}
			}
			
		};
	}
	
	/**
	 * source parent is null
	 * @param entityManager
	 * @param target
	 * @param source
	 */
	protected void move(E source, E target, EntityManager entityManager){}
	
	/**
	 * source parent is not null
	 * @param entityManager
	 * @param target
	 * @param source
	 * @param oldSourceParent
	 */
	protected void move(E source, E target, EntityManager entityManager, E oldSourceParent){}
	
	protected void updateChildren(EntityManager entityManager, E node){
		for(E et : entityManager
				.createQuery(selectChildren, entityClass)
				.setParameter(1, node.getId())
				.getResultList()){
			et.setFullName(node.getFullName() + et.getName());
			et.setXpath(node.getXpath() + et.getId() + '/');
			entityManager.merge(et);
			updateChildren(entityManager, et);
		}
	}
	
	protected E get(TypedQuery<E> tqSelect){
		List<E> list = tqSelect.setMaxResults(1).getResultList();
		if(list.size() == 1){
			return list.get(0);
		}
		return null;
	}
}
